#include "population.hh"
#include "genome.hh"
#include "individual.hh"
#include "cppn/cppn.hh"
#include "cppn/constants.hh"
#include <iostream>
#include <fstream>
#include <unistd.h>
using namespace std;
using namespace evolution;

class IndividualTest : public Individual {
    float fitness = 0;

    cppn::CPPN cppn;

public:
    IndividualTest (shared_ptr<Genome> genome) : Individual(genome), cppn(genome) { }

    static const int DETAIL = 40;

    void think (bool print) {
        fitness = 0;
        int mismatch = 0;
        for (int x = 0; x < DETAIL; ++x) {
            for (int y = 0; y < DETAIL; ++y) {
                float fx = x/(DETAIL/2.0f) - 1.0f;        
                float fy = y/(DETAIL/2.0f) - 1.0f;
                float tmp = 0;
      
                if ((fx < 0.1 && fx > -0.1)) {
                    const float expect = 1;
                    cppn.reset();
                    cppn.set_input(0, 0, fx, fy);
                    float weight = cppn.get_weight();
                    float w = (2 - abs(expect - weight)) / 2.0f;
                    if (print && abs(weight - (expect)) > 0.2f) {
                         cout << fx << ":" << fy 
                              << "\t expect: " << expect << ", got " << weight << endl;
                         mismatch++;
                    }
                    tmp += w*w;

                } if (fx > 0.3 || fx < -0.3) {
                    const float expect = -1;
                    cppn.reset();
                    cppn.set_input(0, 0, fx, fy);
                    float weight = cppn.get_weight();
                    float w = (2 - abs(expect - weight)) / 2.0f;
                    if (print && abs(weight - (expect)) > 0.2f) {
                         cout << fx << ":" << fy 
                              << "\t expect: " << expect << ", got " << weight << endl;
                         mismatch++;
                    }
                    tmp += w*w;

                }

                fitness += tmp;
            }
        }
        
        if (print) cout << "Mismatches: " << mismatch << " of " << DETAIL*DETAIL << endl;
    }

    virtual float calculate_fitness() {
        think(false);
        return get_fitness();
    }

    virtual float get_fitness() const { return fitness; }

    virtual std::shared_ptr<Individual> mate (Individual const* other) const { 
        return shared_ptr<Individual>{new IndividualTest{get_genome()}};
    }

    virtual std::shared_ptr<Individual> clone () const {
        return shared_ptr<Individual>{new IndividualTest{Genome::clone_genome(get_genome())}};
    }

    virtual std::shared_ptr<Individual> twin () const {
        return shared_ptr<Individual>{new IndividualTest{Genome::copy_genome(get_genome())}};
    }

    virtual ~IndividualTest() { }
};

int main (int argc, char** argv) {
    shared_ptr<Genome> a;

    if (argc == 2) {
        ifstream fin(argv[1]);
        a = make_shared<Genome>(cppn::TOTAL_INPUT_NODES);
        fin >> *a;
    
    } else {
        a = Genome::empty_genome(cppn::INITIAL_TOTAL_NODES, cppn::TOTAL_INPUT_NODES);
    }

    Population population;

    for (int i = 0; i < 100; ++i) {
        population.add_individual(shared_ptr<Individual>(new IndividualTest{Genome::clone_genome(a)}));
    }

    IndividualTest* best = nullptr;
    for (int i = 0; i < 500; ++i) {
        cout << "species count: " << population.species_count() << endl;
        population.calculate_fitness();

        auto individual = population.get_best_individual();
        cout << "best individual: " << individual->get_fitness() << "\n\n\n";


        ofstream fout("best");
        fout << *individual->get_genome() << endl;
        best = static_cast<IndividualTest*>(individual.get());

        population.next_generation();
    }
    
    best->think(true);

}
























